package com.example.demo.security.otp.api;

import java.security.SecureRandom;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;

/**
 * Base32编码和解码类，遵循RFC 4648标准。
 * 提供了对字节数据进行Base32编码和解码的方法，以及生成随机Base32字符串的方法。
 *
 * @author houp
 */
public class Base32 {

    private static final int SECRET_SIZE = 10;

    private static final String SEPARATOR = "-";

    private static final SecureRandom RANDOM = new SecureRandom();

    // 单例实例，用于提供全局唯一的Base32编码器/解码器
    private static final Base32 INSTANCE = new Base32("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567");

    /**
     * 获取Base32编码器/解码器的单例实例。
     *
     * @return Base32的单例实例。
     */
    static Base32 getInstance() {
        return INSTANCE;
    }

    // Base32编码使用的字符集
    private String ALPHABET;
    // 字符集对应的数字表示
    private char[] DIGITS;
    // 用于字符映射的掩码
    private int MASK;
    // 用于计算位移的常数
    private int SHIFT;
    // 字符到其索引的映射表
    private HashMap<Character, Integer> CHAR_MAP;

    /**
     * 构造函数，初始化Base32编码器/解码器。
     *
     * @param alphabet Base32编码使用的字符集。
     */
    protected Base32(String alphabet) {
        this.ALPHABET = alphabet;
        DIGITS = ALPHABET.toCharArray();
        MASK = DIGITS.length - 1;
        SHIFT = Integer.numberOfTrailingZeros(DIGITS.length);
        CHAR_MAP = new HashMap<Character, Integer>();
        for (int i = 0; i < DIGITS.length; i++) {
            CHAR_MAP.put(DIGITS[i], i);
        }
    }

    /**
     * 对给定的字符串进行Base32解码。
     *
     * @param encoded 待解码的Base32字符串。
     * @return 解码后的字节数组。
     * @throws DecodingException 如果解码过程中遇到非法字符。
     */
    public static byte[] decode(String encoded) throws DecodingException {
        return getInstance().decodeInternal(encoded);
    }

    /**
     * 实际的解码执行方法。
     *
     * @param encoded 待解码的Base32字符串。
     * @return 解码后的字节数组。
     * @throws DecodingException 如果解码过程中遇到非法字符。
     */
    protected byte[] decodeInternal(String encoded) throws DecodingException {
        // 清理输入字符串，移除空格和分隔符，转换为大写
        encoded = encoded.trim().replaceAll(SEPARATOR, "").replaceAll(" ", "");
        encoded = encoded.replaceFirst("[=]*$", "");

        if (encoded.length() == 0) {
            return new byte[0];
        }
        int encodedLength = encoded.length();
        int outLength = encodedLength * SHIFT / 8;
        byte[] result = new byte[outLength];
        int buffer = 0;
        int next = 0;
        int bitsLeft = 0;
        for (char c : encoded.toCharArray()) {
            if (!CHAR_MAP.containsKey(c)) {
                throw new Base32.DecodingException("Illegal character: " + c);
            }
            buffer <<= SHIFT;
            buffer |= CHAR_MAP.get(c) & MASK;
            bitsLeft += SHIFT;
            if (bitsLeft >= 8) {
                result[next++] = (byte) (buffer >> (bitsLeft - 8));
                bitsLeft -= 8;
            }
        }
        return result;
    }

    /**
     * 对给定的字节数组进行Base32编码。
     *
     * @param data 待编码的字节数组。
     * @return 编码后的字符串。
     */
    public static String encode(byte[] data) {
        return getInstance().encodeInternal(data);
    }

    /**
     * 实际的编码执行方法。
     *
     * @param data 待编码的字节数组。
     * @return 编码后的字符串。
     */
    protected String encodeInternal(byte[] data) {
        if (data.length == 0) {
            return "";
        }
        if (data.length >= (1 << 28)) {
            throw new IllegalArgumentException();
        }

        int outputLength = (data.length * 8 + SHIFT - 1) / SHIFT;
        StringBuilder result = new StringBuilder(outputLength);

        int buffer = data[0];
        int next = 1;
        int bitsLeft = 8;
        while (bitsLeft > 0 || next < data.length) {
            if (bitsLeft < SHIFT) {
                if (next < data.length) {
                    buffer <<= 8;
                    buffer |= (data[next++] & 0xff);
                    bitsLeft += 8;
                } else {
                    int pad = SHIFT - bitsLeft;
                    buffer <<= pad;
                    bitsLeft += pad;
                }
            }
            int index = MASK & (buffer >> (bitsLeft - SHIFT));
            bitsLeft -= SHIFT;
            result.append(DIGITS[index]);
        }
        return result.toString();
    }

    /**
     * Base32解码过程中遇到的错误。
     */
    public static class DecodingException extends Exception {
        public DecodingException(String message) {
            super(message);
        }
    }

    /**
     * 生成一个随机的Base32字符串。
     *
     * @return 随机生成的Base32字符串。
     */
    public static String random() {
        byte[] buffer = new byte[SECRET_SIZE];
        RANDOM.nextBytes(buffer);
        byte[] secretKey = Arrays.copyOf(buffer, SECRET_SIZE);
        return encode(secretKey);
    }
}
